require "trifle/ruby/instrumentation/moped"

require "trifle/ruby/wrappers/event"
require "trifle/ruby/handler"
require "trifle/ruby/app_transaction"
require "trifle/ruby/app_job"

require "net/http"
require "json"

module Trifle
	module Ruby
		class Engine

			def self.start!
				puts "Starting Trifle #{Trifle::Ruby::VERSION} on #{RUBY_VERSION}/#{RUBY_PLATFORM}" unless Trifle::Ruby::Config.is_quiet?
				require "trifle/ruby/integrations/rails"
				require "trifle/ruby/integrations/sidekiq"
				if Engine.should_start_engine?(:queue)
					Engine.subscribe(:queue)
				end
			end

			def self.should_start_engine?(integration)
				defaults = Trifle::Ruby::Config.defaults
				environment = ::Rails.env || ENV["rack_env"] || ENV["RAILS_ENV"]
				config = Trifle::Ruby::Config.load

				passing_config = (config[environment][integration.to_s]["enabled"] rescue false)
				passing_contants = true
				passing_executables = true
				passing_tasks = true

				case integration
				when :rails
					passing_contants = !defaults[:ignored_constants].split(",").any? do |constant_name|
						#::Rails.const_defined? "Console"
						constant_name.to_s.sub(/\A::/,"").split("::").inject(Object) do |namespace, name|
							begin
								result = namespace.const_get(name)
								# unless its a module
								if result.is_a?(Module)
									expected_name = "#{namespace}::#{name}".gsub(/^Object::/, "")
									return false unless expected_name == result.to_s
								end
								result
							rescue NameError
								false
							end
						end
					end
					passing_executables = !defaults[:ignored_executables].split(",").include?(File.basename($0))
					passing_tasks = ((::Rake.application.top_level_tasks rescue []) & defaults[:ignored_tasks].split(",")).empty?
				end
				(passing_config and passing_contants and passing_executables and passing_tasks)
			end

			def self.subscribe(integration)
        is_quiet = Trifle::Ruby::Config.is_quiet?
				puts "Triffle is subscribing #{integration.to_s}" unless is_quiet
				environment = ::Rails.env || ENV["rack_env"] || ENV["RAILS_ENV"]
				config = Trifle::Ruby::Config.load[environment]
				return false if config.nil? or config.empty?

				case integration
				when :rails
					puts "Subscribing :rails start_processing.action_controller" unless is_quiet
					ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.event_type = "application"
						event.identifier = "start_processing"
						event.payload["environment"] = environment
						event.payload["process_id"] = Process.pid
						AppTransaction.current.trace << event unless AppTransaction.current.nil?
					end
					puts "Subscribing :rails render_template.action_view" unless is_quiet
					ActiveSupport::Notifications.subscribe "render_template.action_view" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.payload[:identifier] = event.payload[:identifier].gsub("#{::Rails.root}/", "") unless event.payload[:identifier].nil?
						event.event_type = "view"
						event.identifier = event.payload[:identifier]
						AppTransaction.current.trace << event unless AppTransaction.current.nil?
					end
					puts "Subscribing :rails render_partial.action_view" unless is_quiet
					ActiveSupport::Notifications.subscribe "render_partial.action_view" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.payload[:identifier] = event.payload[:identifier].gsub("#{::Rails.root}/", "") unless event.payload[:identifier].nil?
						event.event_type = "view"
						event.identifier = event.payload[:identifier]
						AppTransaction.current.trace << event unless AppTransaction.current.nil?
					end
					puts "Subscribing :rails sql.active_record" unless is_quiet
					ActiveSupport::Notifications.subscribe "sql.active_record" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.event_type = "active_record"
						event.identifier = event.payload[:name]
						unless payload[:name] == "SCHEMA"
							event.payload["stacktrace"] = event.stacktrace(caller).join("\r\n")
							AppTransaction.current.trace << event unless AppTransaction.current.nil?
						end
					end
					puts "Subscribing :rails query.moped" unless is_quiet
					ActiveSupport::Notifications.subscribe "query.moped" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.event_type = "moped"
						event.identifier = Trifle::Ruby::Instrumentation::Moped.get_collection_with_operation( payload[:ops] )
						event.payload["stacktrace"] = event.stacktrace(caller).join("\r\n")
						event.payload["sql"] = payload[:ops][0].log_inspect unless payload[:ops].nil? or !payload[:ops][0].respond_to?(:log_inspect)
						AppTransaction.current.trace << event unless AppTransaction.current.nil?
					end
					puts "Subscribing :rails process_action.action_controller" unless is_quiet
					ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.event_type = "application"
						event.identifier = "end_processing"
						AppTransaction.current.trace << event unless AppTransaction.current.nil?
					end

				when :sidekiq
					puts "Subscribing :sidekiq jobs.sidekiq.start_processing" unless is_quiet
					ActiveSupport::Notifications.subscribe "jobs.sidekiq.start_processing" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.event_type = "application"
						event.identifier = "start_processing"
						event.payload["environment"] = environment
						event.payload["process_id"] = Process.pid
						AppJob.current.trace << event unless AppJob.current.nil?
					end
					puts "Subscribing :sidekiq render_template.action_view" unless is_quiet
					ActiveSupport::Notifications.subscribe "render_template.action_view" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.payload[:identifier] = event.payload[:identifier].gsub("#{::Rails.root}/", "") unless event.payload[:identifier].nil?
						event.event_type = "view"
						event.identifier = event.payload[:identifier]
						AppJob.current.trace << event unless AppJob.current.nil?
					end
					puts "Subscribing :sidekiq render_partial.action_view" unless is_quiet
					ActiveSupport::Notifications.subscribe "render_partial.action_view" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.payload[:identifier] = event.payload[:identifier].gsub("#{::Rails.root}/", "") unless event.payload[:identifier].nil?
						event.event_type = "view"
						event.identifier = event.payload[:identifier]
						AppJob.current.trace << event unless AppJob.current.nil?
					end
					puts "Subscribing :sidekiq sql.active_record" unless is_quiet
					ActiveSupport::Notifications.subscribe "sql.active_record" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.event_type = "active_record"
						event.identifier = event.payload[:name]
						unless payload[:name] == "SCHEMA"
							event.payload["stacktrace"] = event.stacktrace(caller).join("\r\n")
							AppJob.current.trace << event unless AppJob.current.nil?
						end
					end
					puts "Subscribing :sidekiq query.moped" unless is_quiet
					ActiveSupport::Notifications.subscribe "query.moped" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.event_type = "moped"
						event.identifier = Trifle::Ruby::Instrumentation::Moped.get_collection_with_operation( payload[:ops] )
						event.payload["stacktrace"] = event.stacktrace(caller).join("\r\n")
						event.payload["sql"] = payload[:ops][0].log_inspect unless payload[:ops].nil? or !payload[:ops][0].respond_to?(:log_inspect)
						AppJob.current.trace << event unless AppJob.current.nil?
					end
					puts "Subscribing :sidekiq jobs.sidekiq.end_processing" unless is_quiet
					ActiveSupport::Notifications.subscribe "jobs.sidekiq.end_processing" do |name, start, ending, transaction_id, payload|
						event = Event.new(name, start, ending, transaction_id, payload)
						event.event_type = "application"
						event.identifier = "end_processing"
						AppJob.current.trace << event unless AppJob.current.nil?
					end

				when :queue
					puts "Subscribing :queue exceptions queue" unless is_quiet
					ActiveSupport::Notifications.subscribe "trifle_ruby.exceptions" do |name, start, ending, transaction_id, payload|
						Trifle::Ruby::Handler.submit!("exceptions", payload[:data].to_json)
					end
					puts "Subscribing :queue transactions queue" unless is_quiet
					ActiveSupport::Notifications.subscribe "trifle_ruby.transactions" do |name, start, ending, transaction_id, payload|
						Trifle::Ruby::Handler.submit!("transactions", payload[:data].to_json)
					end
					puts "Subscribing :queue jobs queue" unless is_quiet
					ActiveSupport::Notifications.subscribe "trifle_ruby.jobs" do |name, start, ending, transaction_id, payload|
						Trifle::Ruby::Handler.submit!("jobs", payload[:data].to_json)
					end
				end

				puts "Triffle is subscribed for #{integration.to_s}!" unless is_quiet
			end
		end
	end
end
